﻿Imports Microsoft.Xna.Framework
Imports Microsoft.Xna.Framework.Input
Imports Runa.Foundation.Messaging
Imports Runa.Foundation.CompoentModel

Namespace Global.Runa.UI.Input

    Public Class MouseInputHandler
        Inherits InputHandler

        Private m_state As New MouseState

        Private m_left As ButtonState = ButtonState.Released
        Private m_right As ButtonState = ButtonState.Released
        Private m_wheelDelta As Integer = 0
        Private m_location As Point

        Private m_lastUpdateTime As New TimeSpan(0)

        <MessageMappingAttribute(New String() {"Click", "MouseDown", "MouseUp"}, New MessageType() {MessageType.WM_LBUTTONDOWN, MessageType.WM_LBUTTONDOWN, MessageType.WM_LBUTTONUP})> _
        Public Property LeftButtonState() As ButtonState
            Get
                Return Me.m_left
            End Get
            Set(value As ButtonState)
                If Me.m_left <> value Then
                    If Me.m_left = ButtonState.Released And value = ButtonState.Pressed Then
                        Dim mie = Me.GenerateMouseInputEventArgs(MessageType.WM_LBUTTONDOWN,
                                "LeftButtonState", ButtonState.Released, ButtonState.Pressed)
                        RaiseEvent MouseDown(Me, mie)
                        MyBase.OnDataArrived(Me, mie)
                    Else
                        Dim mie = Me.GenerateMouseInputEventArgs(MessageType.WM_LBUTTONUP,
                                "LeftButtonState", ButtonState.Pressed, ButtonState.Released)
                        RaiseEvent MouseUp(Me, mie)
                        MyBase.OnDataArrived(Me, mie)
                    End If
                End If
                Me.m_left = value
            End Set
        End Property

        <MessageMappingAttribute(New String() {"MouseDown", "MouseUp"}, New MessageType() {MessageType.WM_RBUTTONDOWN, MessageType.WM_RBUTTONUP})> _
        Public Property RightButtonState() As ButtonState
            Get
                Return Me.m_right
            End Get
            Set(value As ButtonState)
                If Me.m_right <> value Then
                    If Me.m_right = ButtonState.Released And value = ButtonState.Pressed Then
                        Dim mie = Me.GenerateMouseInputEventArgs(MessageType.WM_RBUTTONDOWN,
                                "RightButtonState", ButtonState.Released, ButtonState.Pressed)
                        RaiseEvent MouseDown(Me, mie)
                        MyBase.OnDataArrived(Me, mie)
                    Else
                        Dim mie = Me.GenerateMouseInputEventArgs(MessageType.WM_RBUTTONUP,
                               "RightButtonState", ButtonState.Pressed, ButtonState.Released)
                        RaiseEvent MouseUp(Me, mie)
                        MyBase.OnDataArrived(Me, mie)
                    End If
                End If
                Me.m_right = value
            End Set
        End Property

        <MessageMappingAttribute(New String() {"MouseWheel"}, New MessageType() {MessageType.WM_MOUSEWHEEL})> _
        Public Property MouseWheelDelta() As Integer
            Get
                Return Me.m_wheelDelta
            End Get
            Set(value As Integer)
                If value <> 0 Then
                    RaiseEvent MouseWheel(Me, New MouseInputEventArgs(MessageType.WM_MOUSEWHEEL, Me.m_state) With {.MouseWheelDelta = value})
                    MyBase.OnDataArrived(Me, New MouseInputEventArgs(MessageType.WM_MOUSEWHEEL, Me.m_state,
                        New ChangedPropertyInf(Of Object)("MouseWheelDelta", value, 0)) With {.MouseWheelDelta = value})
                End If
                Me.m_wheelDelta = value
            End Set
        End Property

        <MessageMappingAttribute(New String() {"MouseMove"}, New MessageType() {MessageType.WM_MOUSEMOVE})> _
        Public Property Location() As Point
            Get
                Return Me.m_location
            End Get
            Set(value As Point)
                If Me.m_location <> value Then
                    RaiseEvent MouseMove(Me, New MouseInputEventArgs(MessageType.WM_MOUSEMOVE, Me.m_state))
                    MyBase.OnDataArrived(Me, New MouseInputEventArgs(MessageType.WM_MOUSEMOVE, Me.m_state,
                        New ChangedPropertyInf(Of Point)("Location", value, Me.m_location).ConvertToBase()))
                End If
                Me.m_location = value
            End Set
        End Property

        Public Property MouseSensitive() As Integer = 0

        Public Event MouseDown(ByVal sender As Object, ByVal e As MouseInputEventArgs)
        Public Event MouseUp(ByVal sender As Object, ByVal e As MouseInputEventArgs)
        Public Event MouseMove(ByVal sender As Object, ByVal e As MouseInputEventArgs)
        Public Event MouseWheel(ByVal sender As Object, ByVal e As MouseInputEventArgs)

        Public Sub New(ByVal g As Game)
            MyBase.New(g)
        End Sub

        Public Overrides Sub Update(gameTime As Microsoft.Xna.Framework.GameTime)
            If (gameTime.TotalGameTime - Me.m_lastUpdateTime).TotalMilliseconds >= Me.MouseSensitive Then
                If Me.Game.IsActive And Me.Enabled Then
                    Dim oldWheel = Me.m_state.ScrollWheelValue
                    Me.m_state = Microsoft.Xna.Framework.Input.Mouse.GetState()
                    If Me.Game.GraphicsDevice.Viewport.TitleSafeArea.Contains(New Point(Me.m_state.X, Me.m_state.Y)) Then
                        Me.LeftButtonState = Me.m_state.LeftButton
                        Me.RightButtonState = Me.m_state.RightButton
                        Me.MouseWheelDelta = Me.m_state.ScrollWheelValue - oldWheel
                        Me.Location = New Point(Me.m_state.X, Me.m_state.Y)
                    End If
                    Me.m_lastUpdateTime = gameTime.TotalGameTime
                End If
            End If
        End Sub

        Private Function GenerateMouseInputEventArgs(Of T)(ByVal msg As MessageType, ByVal pName As String, ByVal oldValue As T, ByVal newValue As T) As MouseInputEventArgs
            Return New MouseInputEventArgs(msg, Me.m_state, New ChangedPropertyInf(Of Object)(pName, newValue, oldValue))
        End Function

    End Class

End Namespace